package org.xs.ibatis.session;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import javax.sql.DataSource;

import org.apache.ibatis.exceptions.IbatisException;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.support.JdbcAccessor;

@SuppressWarnings("unchecked")
public final class SpringSqlSession extends JdbcAccessor implements SqlSession, InitializingBean {
   private SqlSession delegate;

    private boolean runningInTransaction = false;
    private Connection connection;
    SpringSqlSession() {}

    SpringSqlSession(SqlSessionFactory sessionFactory, DataSource dataSource,Connection connection,boolean runningInTransaction)  {
		this.connection = connection;
    	this.delegate = sessionFactory.openSession(connection);
        setDataSource(dataSource);
        afterPropertiesSet();
        this.runningInTransaction = runningInTransaction;
    }

    public void setDelegate(SqlSession delegate) {
        this.delegate = delegate;
       
    }
    
    public Connection getConnection() {
        return delegate.getConnection();
    }


    public boolean isRunningInTransaction() {
        return runningInTransaction;
    }

    public void setRunningInTransaction(boolean runningInTransaction) {
        this.runningInTransaction = runningInTransaction;
    }

    public void afterPropertiesSet() {
        if (delegate == null) {
            throw new IllegalArgumentException(
                    "Property 'delegate' is required");
        }

        if (delegate.getConnection() == null) {
            throw new IllegalArgumentException(
                    "Property 'connection' is required");
        }

        super.afterPropertiesSet();
    }

    public Object selectOne(String statement) {
    	try{
    		return selectOne(statement, null);
    	}finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}
        
    }

    public Object selectOne(final String statement, final Object parameter) {
        try {
            return delegate.selectOne(statement, parameter);
        }
        catch (IbatisException ie) {
            throw translateIbatisException(ie, "selectOne", statement);
        }finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}
    }

    public List selectList(String statement) {
    	try{
            return selectList(statement, null);

    	}finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}
    }

    public List selectList(String statement, Object parameter) {
    	try{
    		return selectList(statement, parameter, RowBounds.NO_ROW_OFFSET,
            		RowBounds.NO_ROW_LIMIT);
        
    	}finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}
     }

    public List selectList(final String statement, final Object parameter,
            final int offset, final int limit) {
        try {
            return delegate.selectList(statement, parameter,new RowBounds(offset,limit));
        }
        catch (IbatisException ie) {
            throw translateIbatisException(ie, "selectList", statement);
        }finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}
    }

    public void select(String statement, Object parameter,ResultHandler handler) {
    	try{
    		select(statement, parameter, RowBounds.NO_ROW_OFFSET,
            		RowBounds.NO_ROW_LIMIT, handler);
        
    	}finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}
    }

    public void select(final String statement, final Object parameter,
            final int offset, final int limit, final ResultHandler handler) {
        try {
            delegate.select(statement, parameter, new RowBounds(offset,limit), handler);
        }
        catch (IbatisException ie) {
            throw translateIbatisException(ie, "select", statement);
        }finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}
    }

    public int insert(String statement) {
    	try {
    		return insert(statement, null);
		} finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}
      }

    public int insert(String statement, Object parameter) {
        try {
            return delegate.insert(statement, parameter);
        }
        catch (IbatisException ie) {
            throw translateIbatisException(ie, "insert", statement);
        }finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}

    }

    public int update(String statement) {
    	try {

            return update(statement, null);
        
		} finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}}

    public int update(final String statement, final Object parameter) {
        try {
            return delegate.update(statement, parameter);
        }
        catch (IbatisException ie) {
            throw translateIbatisException(ie, "update", statement);
        }finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}
    }

    public int delete(String statement) {
    	try{

            return update(statement, null);
        
    	}finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}}

    public int delete(String statement, Object parameter) {
        try {
            return delegate.delete(statement, parameter);
        }
        catch (IbatisException ie) {
            throw translateIbatisException(ie, "delete", statement);
        }finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}
    }

    public void commit() {
        if (delegate != null) {
            delegate.commit();
        }
    }

    public void commit(boolean force) {
        if (delegate != null) {
            delegate.commit(force);
        }
    }

    public void rollback() {
        if (delegate != null) {
            delegate.rollback();
        }
    }

    public void rollback(boolean force) {
        if (delegate != null) {
            delegate.rollback(force);
        }
    }

    public void close() {
        if (delegate != null) {
            delegate.close();
        }
    }

    public Configuration getConfiguration() {
        return delegate == null ? null : delegate.getConfiguration();
    }

    public <T> T getMapper(Class<T> type) {
        return delegate == null ? null : delegate.getMapper(type);
    }

    // do we want / need to translate the IbatisException hierarchy into Spring's?
    private RuntimeException translateIbatisException(IbatisException ie,
            String methodName, String statement) {
        if (ie.getCause() instanceof SQLException) {
            return getExceptionTranslator().translate(methodName, statement,
                    (SQLException) ie.getCause());
        }
        else {
            return ie;
        }
    }

	public void select(String statement, Object parameter, RowBounds rowBounds,
			ResultHandler handler) {
		try {
			delegate.select(statement, parameter, rowBounds, handler);
		} finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}
	}

	public List selectList(String statement, Object parameter,
			RowBounds rowBounds) {
		try {
			return delegate.selectList(statement, parameter, rowBounds);
			
		} finally{
    		if(!isRunningInTransaction()){
    			try {
				connection.close();
				} catch (SQLException e) {
				}
    			delegate.close();
    		}
    	}
	}

	public Statement queryForStatement(String arg0) {
		return null;
	}

	public Statement queryForStatement(String arg0, Object arg1) {
		return null;
	}


}
